/*----------------------------------------------------------------------------
 *      RL-ARM - RTX
 *----------------------------------------------------------------------------
 *      Name:    Water Dispenser.C
 *      Purpose: RTX example program
 *----------------------------------------------------------------------------
 *      This code is part of the RealView Run-Time Library.
 *      Copyright (c) 2004-2011 KEIL - An ARM Company. All rights reserved.
 *---------------------------------------------------------------------------*/

#include <RTL.h>
#include <91x_lib.H>
#include "LCD.h"

#ifndef MAX_CLOSE_LENGTH
#define MAX_CLOSE_LENGTH 50
#endif
#ifndef MAX_OPEN_LENGTH
#define MAX_OPEN_LENGTH 100
#endif
// auto turn off the light in 50 * 200ms = 10s
#ifndef AUTO_OFF
#define AUTO_OFF 50
#endif
// tunning down the light for 25 * 200ms = 5s
#ifndef DIMMING_LENGTH
#define DIMMING_LENGTH 25
#endif
unsigned char A0,A1; //A0 - Button 3.5, A1 - Button 3.6
unsigned char B0 = 0,B1 = 0,B2 = 0,B3 = 0,B4 = 0,B5 = 0,B6 = 0,B7 = 0;
// B0-B7 represent LED's 0 through 7
unsigned int i, door_counter, dim_counter, off_counter, alarm_counter, dimmer;

typedef enum { DOOR_CLOSED, DOOR_OPEN } DOOR_State;
typedef enum { AIRCON_OFF, AIRCON_ON } AIRCON_State;
typedef enum { ALARM_OFF, ALARM_ON } ALARM_State;
typedef enum { LIGHT_OFF, LIGHT_ON, LIGHT_DIMMING} LIGHT_State;

DOOR_State door_state;
AIRCON_State aircon_state;
ALARM_State alarm_state;
LIGHT_State light_state;

//Function to read input
void read_input() {
    //BUTTON_3_5:
    A0 = !(GPIO3->DR[0x080]>>5); // Returns 1 when pressed and 0 when released
    //BUTTON_3_6:
    A1 = !(GPIO3->DR[0x100]>>6); // Returns 1 when pressed and 0 when released
}

//Function to write to led
void write_led() {
    unsigned char mask = 0;

    mask  = (B0<<0);
    mask |= (B1<<1);
    mask |= (B2<<2);
    mask |= (B3<<3);
    mask |= (B4<<4);
    mask |= (B5<<5);
    mask |= (B6<<6);
    mask |= (B7<<7);

    GPIO7->DR[0x3FC] = mask;
}

//Function to print an unsigned char value on the LCD Display
void print_uns_char (unsigned char value) {
    int flag = 0,i;
    char c[4];
    do {
       int rem = value%10;
       value = value/10;
       c[flag] = rem+48;
       flag++;
    } while(value>0);
    for (i = flag-1; i >= 0; i--)
        LCD_putc(c[i]);
}


OS_TID Light_controller_id;
OS_TID Light_dimmer_id;
// Declare variable t_Lighting to store the task id

void handle_door_state() {
    switch (door_state) {
        case DOOR_CLOSED:
            if (A0) {
                door_state = DOOR_OPEN;
                door_counter = 0;
            }
            break;
        case DOOR_OPEN:
            if (A0) {
                door_state = DOOR_CLOSED;
                door_counter = 0;
            }
            break;
        default:
            door_state = DOOR_CLOSED;
    }
}

void handle_aircon_state() {
    switch (aircon_state) {
        case AIRCON_OFF:
            if (A1) {
                aircon_state = AIRCON_ON;
            }
            break;
        case AIRCON_ON:
            if (A1) {
                aircon_state = AIRCON_OFF;
            }
            break;
        default:
            aircon_state = AIRCON_OFF;
    }
}

void handle_alarm_state() {
    switch (alarm_state) {
        case ALARM_OFF:
            if (aircon_state == AIRCON_ON && door_state == DOOR_OPEN) {
                alarm_state = ALARM_ON;
                alarm_counter = 0;
            }
            break;
        case ALARM_ON:
            if (aircon_state == AIRCON_OFF || door_state == DOOR_CLOSED) {
                alarm_state = ALARM_OFF;
            }
            break;
        default:
            alarm_state = ALARM_OFF;
    }
    if (alarm_state == ALARM_ON) {
        if (alarm_counter % 2 == 0) {
            B6 = 1;
            B7 = 1;
        } else {
            B6 = 0;
            B7 = 0;
        }
        alarm_counter++;
        if (alarm_counter == 2) {
            alarm_counter = 0;
        }
    } else {
        B6 = 0;
        B7 = 0;
    }
}

void handle_light_state() {
    switch (light_state) {
        case LIGHT_OFF:
            if (A0) {
                light_state = LIGHT_ON;
            }
            break;
        case LIGHT_ON:
            if ((door_state == DOOR_CLOSED && aircon_state == AIRCON_ON) ||
                (door_state == DOOR_CLOSED && door_counter >= MAX_CLOSE_LENGTH) ||
                (door_state == DOOR_OPEN && door_counter >= MAX_OPEN_LENGTH)) {
                light_state = LIGHT_DIMMING;
                dim_counter = 0;
                off_counter = 0;
            } else {
                door_counter++;
            }
            break;
        case LIGHT_DIMMING:
            if (A0) {
                light_state = LIGHT_ON;
            } else if (door_state == DOOR_CLOSED && aircon_state == AIRCON_ON &&
                off_counter >= AUTO_OFF) {
                light_state = LIGHT_OFF;
            } else if (door_state == DOOR_CLOSED && aircon_state == AIRCON_ON) {
                off_counter++;
            }
            if (dim_counter < DIMMING_LENGTH) {
                dim_counter++;
            }
            break;
        default:
            light_state = LIGHT_OFF;
    }
}

void write_lcd() {
    LCD_cls();
    switch (door_state) {
        case DOOR_CLOSED:
            LCD_puts("DOOR CLOSED");
            break;
        case DOOR_OPEN:
            LCD_puts("DOOR OPEN");
            break;
        default:
            LCD_puts("DOOR CLOSED");
    }
    LCD_gotoxy(1,2);  // switch to the second line
    switch (aircon_state) {
        case AIRCON_ON:
            LCD_puts("AIRCON ON");
            break;
        case AIRCON_OFF:
            LCD_puts("AIRCON OFF");
            break;
        default:
            LCD_puts("AIRCON OFF");
    }
    LCD_cur_off();
}

__task void TASK_SENSOR(void) {
    // Copy task period value in milliseconds here
    const unsigned int taskperiod = 200;
    os_itv_set(taskperiod);
    LCD_on();
    while (1) {
        read_input();
        handle_door_state();
        handle_aircon_state();
        handle_alarm_state();
        handle_light_state();
        write_lcd();
        os_itv_wait();
    } // while (1)
}

void set_lights(int num) {
    B0 = num;
    B1 = num;
    B2 = num;
    B3 = num;
    B4 = num;
    B5 = num;
}

void handle_light_level() {
    switch (light_state) {
        case LIGHT_OFF:
            set_lights(0);
            break;
        case LIGHT_ON:
            set_lights(1);
            break;
        case LIGHT_DIMMING:
            if (dimmer % DIMMING_LENGTH - 1 > dim_counter) {
                set_lights(1);
            } else {
                set_lights(0);
            }
            dimmer++;
            if (dimmer == DIMMING_LENGTH) {
                dimmer = 0;
            }
            break;
        default:
            set_lights(0);
    }
}

__task void TASK_DIMMER(void) {
    // Copy task period value in milliseconds here
    const unsigned int taskperiod = 1;
    os_itv_set(taskperiod);
    while (1) {
        handle_light_level();
        write_led();
        os_itv_wait();
    } // while (1)
}


/*----------------------------------------------------------------------------
 *        Task 'init': Initialize
 *---------------------------------------------------------------------------*/
__task void init (void) {

    /* Configuring LED                     */
    SCU->GPIOOUT[7]  = 0x5555;
    GPIO7->DDR       = 0xFF;
    GPIO7->DR[0x3FC] = 0x00;

    /* LCD Setup                           */
    GPIO8->DDR       = 0xFF;
    GPIO9->DDR       = 0x07;

    /* Port 3 setup for button 3.5 and 3.6 */
    SCU->GPIOIN[3]  |= 0x60;
    SCU->GPIOOUT[3] &= 0xC3FF;
    GPIO3->DDR      &= 0x9F;

    LCD_init(); // Initialize LCD
    LCD_cur_off(); // Remove LCD cursor
    LCD_cls(); // Clearing LCD screen

    //Launch the task in the following manner
    //taskname_id = os_tsk_create (State_Machine_Name, 0);
    door_counter = 0;
    dim_counter = 0;
    off_counter = 0;
    light_state = LIGHT_OFF;
    door_state = DOOR_CLOSED;
    aircon_state = AIRCON_OFF;
    Light_controller_id = os_tsk_create(TASK_SENSOR, 0);
    Light_dimmer_id = os_tsk_create(TASK_DIMMER, 0);
    os_tsk_delete_self ();
}


/*----------------------------------------------------------------------------
 *        Main: Initialize and start RTX Kernel
 *---------------------------------------------------------------------------*/
int main (void) {
    os_sys_init (init);                  /* Initialize RTX and start init    */
    return 0;
}

/*----------------------------------------------------------------------------
 * end of file
 *---------------------------------------------------------------------------*/
